'''
#由于abs函数实际上是定义在import builtins模块中的，所以要让修改abs变量的指向在其它模块也生效，要用import builtins; builtins.abs = 10
import builtins; builtins.abs = 10
#输出的abs是10
print(abs)
'''


#把函数作为参数传入，这样的函数称为高阶函数，函数式编程就是指这种高度抽象的编程范式
#map()函数接收两个参数，一个是函数，一个是Iterable，map将传入的函数依次作用到序列的每个元素，并把结果作为新的Iterator返回
#例如
def f(x):
    return x * x
r = map(f,[1,2,3,4,5,6,7,8,9])
list(r)

list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
#reduce把一个函数作用在一个序列[x1, x2, x3, ...]上，这个函数必须接收两个参数，reduce把结果继续和序列的下一个元素做累积计算
#reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
from functools import reduce
def add(x, y):
    return x + y
reduce(add, [1, 3, 5, 7, 9])

#把序列变换成整数

def fn(x,y):
    return x *10 + y

reduce(fn,[1,3,5,7,9])

#将str转换成int
def char2num(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]

r = reduce(fn,map(char2num,'12356'))
print(r)

#整理成一个str2int的函数

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x,y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn,map(char2num(s)))

#----------简化---------#
def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

#------------练习-------------#

#把用户输入的不规范的英文名字，变为首字母大写，其他小写的规范名字
def normalize(name):
    return name.title()

L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)

#请编写一个prod()函数，可以接受一个list并利用reduce()求积
def prod(L):
    def mult(x,y):
        return x * y
    s = reduce(mult, L)
    return s

print('3 * 5 * 7 * 9 =', prod([3, 5, 7, 9]))
if prod([3, 5, 7, 9]) == 945:
    print('测试成功!')
else:
    print('测试失败!')

#map和reduce编写一个str2float函数，把字符串'123.456'转换成浮点数123.456
def str2float(s):
    for n in range(len(s)):
        if s[n] == '.':
            count = len(s) - n - 1
            break
    s = s.split('.',1)
    def fn(x,y):
        return x * 10 + y
    def char2num(s):
        DIGITS =  {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
        return DIGITS[s]
    num1 = reduce(fn,map(char2num,s[0]))
    num2 = reduce(fn,map(char2num,s[1]))/(10**count)
    return num1 + num2

print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
    print('测试成功!')
else:
    print('测试失败!')

#-------------filter-------------#
#和map()类似，filter()也接收一个函数和一个序列。和map()不同的是，filter()把传入的函数依次作用于每个元素，然后根据返回值是True还是False决定保留还是丢弃该元素

#在一个list中，删掉偶数，只保留奇数

def is_odd(n):
    return n % 2 == 1
list(filter(is_odd,[1,2,3,4,5,6,7,8,9,10,23]))

#把一个序列中的空字符串删掉
#如果一个字符串去掉首尾的空格之后，与自己本身做与运算是1 ，说明这个字符串不含有空格
def not_empty(s):
    return s and s.strip()
list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 结果: ['A', 'B', 'C']

#filter()函数返回的是一个Iterator，也就是一个惰性序列，所以要强迫filter()完成计算结果，需要用list()函数获得所有结果并返回list

def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n

def _not_divisible(n):
    return lambda x: x % n > 0

def primes():
    yield 2
    it = _odd_iter()
    while True:
        n = next(it)
        yield n
        it = filter(_not_divisible(n),it)

for n in primes():
    if n < 1000:
        print(n)
    else:
        break

#---------------------练习-----------------#
#回数是指从左向右读和从右向左读都是一样的数，例如12321，909。请利用filter()筛选出回数

def is_palindrome(n):
    s = str(n)
    k = list(range(len(s)//2))
    for i in k:
        if s[i] != s[len(s) - i - 1]:
            return False
    return True

#更简洁的写法
def is_palindrome(n):
    s = str(n)
    return s == s[::-1]#反转字符串

output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:
    print('测试成功!')
else:
    print('测试失败!')





